package com.fingard.dsp.bank.directbank.mipay01;

import com.cfca.util.pki.encoders.UrlBase64;
import com.fingard.dsp.bank.directConfig.ActSetItem;
import com.fingard.dsp.bank.directbank.DirectBase;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;

import javax.crypto.Cipher;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

public class MIPAY01Base extends DirectBase {
    protected String getCharset() {
        return getBankConfig().getCharset("UTF-8");
    }

    public String encrypt(PublicKey key, String plain) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] date = cipher.doFinal(plain.getBytes("utf-8"));
        return Base64.encodeBase64String(date);
    }

    public PublicKey getPublicKey(String key) {
        try {
            byte[] keyBytes = Base64.decodeBase64(key);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(keySpec);
            return publicKey;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static PrivateKey getPrivateKey(String key) {
        try {
            byte[] keyBytes = Base64.decodeBase64(key.getBytes("UTF-8"));
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
            return privateKey;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将MAP转换为post请求串
     *
     * @return
     */
    public String convertMapToStr(Map<String, Object> params) {
        String reqStr = "";
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        for (String key : keys) {
            if (!params.get(key).equals("")) {
                if (reqStr.length() > 0) {
                    reqStr += "&";
                }
                reqStr += key + "=" + params.get(key);
            }
        }
        return reqStr;
    }

    public static Map<String, String> convertToMap(String srcStr) {
        Map<String, String> params = new HashMap<String, String>();

        String[] nodes = srcStr.split("&");
        for (String node : nodes) {
            int index = node.indexOf("=");

            String key = node.substring(0, index);
            String value = node.substring(index + 1);

            params.put(key, value);
        }

        return params;
    }

    /**
     * 签名
     */
    public String getSign(String tmpStr, ActSetItem actItem) {
        String sign = "";
        try {
            WriteBankLogLn("待签名字段：" + tmpStr);
            PrivateKey privateKey = getPrivateKey(actItem.ownKeyStorePath);
            Signature signature = Signature.getInstance("SHA1WithRSA");
            signature.initSign(privateKey);
            signature.update(tmpStr.getBytes(getCharset()));
            byte[] signed = signature.sign();
            sign = new String(UrlBase64.encode(signed), getCharset());
        } catch (Exception e) {
            WriteBankLogLn("签名失败", e);
        }
        return sign;
    }

    public String[] sendToBank(Map<String, Object> args, String serverUrl) throws Exception {
        String[] retStr = new String[]{"", ""};
        HttpPost post = new HttpPost(serverUrl);
        post.setHeader("Content-type", "application/x-www-form-urlencoded;charset=UTF-8");
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        for (Map.Entry<String, Object> entry : args.entrySet()) {
            nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
        }
        HttpEntity entity = new UrlEncodedFormEntity(nvps, getCharset());
        post.setEntity(entity);
        RequestConfig.Builder customReqConf = RequestConfig.custom();
        customReqConf.setConnectTimeout(10000);
        customReqConf.setSocketTimeout(10000);
        //使用代理
        boolean enableProxy = getBankConfig().getValueAsBool("enableProxy");
        if (enableProxy) {
            String proxyIP = getBankConfig().getValueAsString("proxyIP");
            int proxyPort = Integer.parseInt(getBankConfig().getValueAsString("proxyPort"));
            HttpHost proxy = new HttpHost(proxyIP, proxyPort, "http");
            customReqConf.setProxy(proxy);
        }
        post.setConfig(customReqConf.build());
        HttpClient httpClient = createSSLInsecureClient();
        HttpResponse httpResponse = httpClient.execute(post);
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        if (statusCode == 200) {
            String contentType = null;
            if (httpResponse.getEntity().getContentType() != null) {
                contentType = httpResponse.getEntity().getContentType().toString();
            }
            InputStream inputStream = httpResponse.getEntity().getContent();
            int len;
            if (!StringUtils.isBlank(contentType) && contentType.contains("text/html")) {
                try {
                    StringBuffer sb = new StringBuffer();
                    if (inputStream != null) {
                        sb.setLength(0);
                        InputStreamReader reader = new InputStreamReader(inputStream, "utf-8");
                        char[] cbuf = new char[1024];
                        while ((len = reader.read(cbuf, 0, cbuf.length)) != -1) {
                            sb.append(new String(cbuf, 0, len));
                        }
                        reader.close();
                        retStr[1] = sb.toString();
                        return retStr;
                    }
                } finally {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                }

            }
        } else {
            throw new Exception("Unexpected server response: " + statusCode);
        }
        return retStr;
    }

    private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                return true;
            }
        }).build();
        SSLConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        });
        return HttpClients.custom().setSSLSocketFactory(sslSF).build();
    }
}
